package com.baturu.vin.honda.service.impl;

import com.baturu.carvin.service.CarTypeService;
import com.baturu.carvin.service.impl.AppConfigLoader;
import com.baturu.kit.kit.guava2.Sets2;
import com.baturu.vin.base.dto.BasePartsImageInfoDTO;
import com.baturu.vin.base.service.impl.EPCAnalyser;
import com.baturu.vin.dto.*;
import com.baturu.vin.dto.parts.PartsAssemblyDTO;
import com.baturu.vin.dto.query.EPCPartsCodeResultDTO;
import com.baturu.vin.honda.constant.HondaDataVersionConstant;
import com.baturu.vin.honda.dal.dao.*;
import com.baturu.vin.honda.dal.entity.HondaConditionCEntity;
import com.baturu.vin.honda.dal.entity.HondaHpartAndHmodEntity;
import com.baturu.vin.honda.dal.entity.HondaPartsColorInfoEntity;
import com.baturu.vin.honda.dal.entity.HondaSpecialNameEntity;
import com.baturu.vin.honda.dto.*;
import com.baturu.vin.kit.StreamKit;
import com.baturu.vin.service.EPCQueryService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;

/**
 * honda解析器
 *
 * @author jiangxinlei
 * @Time 2017/6/6.
 */
@Slf4j
@Service("hondaEPCAnalyser")
public class HondaEPCAnalyser extends EPCAnalyser<HondaVinInfoDTO, HondaPartsMappingDTO, HondaPartsInfoDTO, EPCCarTypeDTO> {

    private static Set<String> ouges = Sets.newHashSet("ACURA NSX", "CDX", "ILX", "ILX HYBRID", "MDX", "NSX", "RDX", "RL", "RLX", "TL", "TLX", "ZDX", "2.5TL", "3.2TL", "3.5RL");
    @Autowired
    private HondaVinDAO hondaVinDAO;
    @Autowired
    private HondaPartsMappingDAO hondaPartsMappingDAO;
    @Autowired
    private HondaPartDAO hondaPartDAO;
    @Autowired
    private HondaPartsCarTypeDAO hondaPartsCarTypeDAO;
    @Autowired
    private AppConfigLoader appConfigLoader;
    @Autowired
    private HondaPartsImageDAO hondaPartsImageDAO;
    @Autowired
    private HondaImageDAO hondaImageDAO;
    @Autowired
    private CarTypeService carTypeService;
    @Autowired
    private HondaAssemblyDAO hondaAssemblyDAO;
    @Autowired
    private HondaBtrPartsDAO hondaBtrPartsDAO;
    @Autowired
    private EPCQueryService epcQueryService;

    @Override
    public HondaVinInfoDTO analyseVinCode(String vinCode) {
        //数据部说所有车都是美规车
//        if (vinCode.startsWith("L") && !VinValidator.fullValidate(vinCode)) {
//            log.info("被美规车过滤");
//            return null;
//        }
        String vinSub9 = vinCode.substring(9);
        List<HondaVinInfoDTO> vinInfos = hondaVinDAO.getVinInfo(vinCode.substring(0, 11), vinSub9);
        if (CollectionUtils.isEmpty(vinInfos)) {
            String vinPre11 = vinCode.substring(0, 8) + "*" + vinCode.substring(9, 11);
            vinInfos = hondaVinDAO.getVinInfo(vinPre11, vinSub9);
        }
        if (CollectionUtils.isEmpty(vinInfos)) {
            return null;
        }
        HondaVinInfoDTO hondaVinInfoDTO = vinInfos.stream().max(Comparator.comparing(HondaVinInfoDTO::getDisk)).get();
//        HondaVinInfoDTO hondaVinInfoDTO = vinInfos.stream().max(Comparator.comparingInt(d->Integer.parseInt(d.getDisk()))).get();
        String maxDisk = hondaVinInfoDTO.getDisk();

        Set<HondaVinMultiInfoDTO> hondaVinMultiInfoDTOS = vinInfos.stream()
                .filter(p -> p.getDisk().equals(maxDisk))
                .map(o -> HondaVinMultiInfoDTO.builder()
                        .hmodtyp(o.getHmodtyp())
                        .carea(o.getCarea())
                        .cmodtypfrm(o.getCmodtypfrm())
                        .cmodtypengn(o.getCmodtypengn())
                        .build())
                .collect(Collectors.toSet());


        hondaVinInfoDTO.setVinMultiInfoDTO(hondaVinMultiInfoDTOS);
        hondaVinInfoDTO.setVinCode(vinCode);

        hondaVinInfoDTO.setSuffix8(vinSub9);
        Set<Integer> hmod = Sets2.transform(hondaVinMultiInfoDTOS, HondaVinMultiInfoDTO::getHmodtyp).stream().filter(Objects::nonNull).collect(Collectors.toSet());
        if (hmod.isEmpty()) {
            hmod.add(hondaVinInfoDTO.getHmodtyp());
        }
        hondaVinInfoDTO.setHmodtyps(hmod);
        hondaVinInfoDTO.setNplblks(getNplblk1(hondaVinInfoDTO));
        return hondaVinInfoDTO;
    }


    private Set<String> getNplblk1(HondaVinInfoDTO hondaVinInfoDTO) {
        Set<String> nplblkForFilter = hondaVinDAO.getNplblkForFilter(hondaVinInfoDTO.getNpl(), hondaVinInfoDTO.getHmodtyps());
        nplblkForFilter.add("-1");
        return nplblkForFilter;
    }

    @Override
    public List<EPCCarTypeDTO> queryCarTypes(HondaVinInfoDTO analyseResult) {
        String vinCode = analyseResult.getVinCode();
        String btrId = analyseResult.getBtrId();
        if (StringUtils.isBlank(btrId)) {
            return Collections.emptyList();
        } else {
            EPCCarTypeDTO epcCarTypeDTO = new EPCCarTypeDTO();
            epcCarTypeDTO.setVinCode(vinCode);
            epcCarTypeDTO.setBtrId(btrId);
            return Lists.newArrayList(epcCarTypeDTO);
        }
    }

    @Override
    public List<HondaPartsMappingDTO> queryPartsMappings(Set<String> btrPartsNames, HondaVinInfoDTO analyseResult) {

//        return StreamKit.partitionStream(btrPartsNames, SEGMENT_QUERY_SIZE)
//                .flatMap(z -> hondaPartsMappingDAO.findPartsMappingByPartsName(new HashSet<>(z)).stream())
//                .distinct().collect(Collectors.toList());
        return StreamKit.partitionStream(btrPartsNames, SEGMENT_QUERY_SIZE)
                .flatMap(z -> hondaPartsMappingDAO.findV2PartsMappingByPartsName(new HashSet<>(z), analyseResult.getNpl()).stream())
                .distinct().collect(Collectors.toList());
    }

    @Override
    public List<HondaPartsInfoDTO> queryPartsInfoByPartsMapping(List<HondaPartsMappingDTO> partsMappings, HondaVinInfoDTO analyseResult) {
        if (analyseResult == null) {
            return Collections.emptyList();
        }
//        Set<String> partsCodes = partsMappings.stream().map(HondaPartsMappingDTO::getPartsCode).collect(Collectors.toSet());
        Set<String> partsIds = partsMappings.stream().map(HondaPartsMappingDTO::getPartsId).collect(Collectors.toSet());

//        return StreamKit.partitionStream(partsCodes, SEGMENT_QUERY_SIZE)
//                .flatMap(v -> StreamKit.partitionStream(analyseResult.getNplblks(), SEGMENT_QUERY_SIZE).flatMap(z -> hondaPartDAO.findPartsInfo(new HashSet<>(v), analyseResult.getNpl(), analyseResult.getDisk(), z).stream()).collect(Collectors.toSet()).stream())
//                .distinct().collect(Collectors.toList());
        return StreamKit.partitionStream(partsIds, SEGMENT_QUERY_SIZE)
                .flatMap(v -> StreamKit.partitionStream(analyseResult.getNplblks(), SEGMENT_QUERY_SIZE).flatMap(z -> hondaPartDAO.findPartsInfoByPartsId(new HashSet<>(v), analyseResult.getNpl(), analyseResult.getDisk(), z).stream()).collect(Collectors.toSet()).stream())
                .distinct().collect(Collectors.toList());
    }


    @Override
    public Set<String> filterEPCPartsNamesByVinCode(String vinCode, Set<String> epcPartsNames) {
        HondaVinInfoDTO hondaVinInfoDTO = analyseVinCode(vinCode);
        if (hondaVinInfoDTO == null) {
            return Collections.emptySet();
        }

        List<HondaPartsMappingDTO> hondaPartsMappingDTOS = queryPartsMappings(epcPartsNames, hondaVinInfoDTO);
        Set<String> partsCode = queryPartsInfoByPartsMapping(hondaPartsMappingDTOS, hondaVinInfoDTO).stream()
                .map(HondaPartsInfoDTO::getNpartgenu).collect(Collectors.toSet());
        return hondaPartsMappingDTOS.stream()
                .filter(d -> partsCode.contains(d.getPartsCode()))
                .map(HondaPartsMappingDTO::getBtrPartsName)
                .collect(Collectors.toSet());
    }

    @Override
    public List<HondaPartsInfoDTO> queryPartsInfoByPartsCode(Set<String> partsCodes) {
        Set<HondaPartsInfoDTO> partsInfo = hondaPartDAO.findPartsInfoByCode(partsCodes);
        return Lists.newArrayList(partsInfo);
    }

    @Override
    public List<HondaPartsInfoDTO> queryPartsInfoByPartsCode(Set<String> partsCodes, HondaVinInfoDTO analyseResult) {
        if (analyseResult == null) {
            return Collections.emptyList();
        }
        Set<HondaPartsInfoDTO> partsInfo = hondaPartDAO.findPartsInfo(partsCodes, analyseResult.getNpl(), analyseResult.getDisk(), analyseResult.getNplblks());
        return Lists.newArrayList(partsInfo);
    }

    @Override
    public List<HondaPartsInfoDTO> filterPartsInfoByVinAnalyseResult(List<HondaPartsInfoDTO> partsInfos, HondaVinInfoDTO analyseResult) {
        //导致带出率下降，epc无此规则
//        partsInfos.removeIf(p -> !filterByStrtEnd(p, analyseResult));

        if (CollectionUtils.isEmpty(partsInfos)) {
            return Collections.emptyList();
        }


        Set<Integer> hparts = partsInfos.stream().map(HondaPartsInfoDTO::getHpartplblk).collect(Collectors.toSet());
        List<HondaHpartAndHmodEntity> hmodtypsAll;
        if (CollectionUtils.isNotEmpty(hparts)) {
            hmodtypsAll = hondaPartDAO.queryAllhmodtyp(analyseResult.getDisk(), hparts);
        } else {
            hmodtypsAll = Lists.newArrayList();
        }
        Map<Integer, List<HondaHpartAndHmodEntity>> groupByHpart = hmodtypsAll.stream().collect(Collectors.groupingBy(HondaHpartAndHmodEntity::getHpartplblk));

        List<HondaConditionCEntity> conditionCEntities = hondaPartDAO.queryConditionC(analyseResult.getDisk(), partsInfos, analyseResult.getHmodtyps());
        boolean conditionCIsEmpty = conditionCEntities.isEmpty();
        Map<String, List<HondaConditionCEntity>> hmod2Condition = new HashMap<>();
        if (!conditionCIsEmpty) {
            conditionCEntities.forEach(c -> {
                String key = c.getNpl() + "_" + c.getNplblk();
                List<HondaConditionCEntity> entities = hmod2Condition.computeIfAbsent(key, k -> new ArrayList<>());
                entities.add(c);
            });
        }
        List<HondaPartsInfoDTO> collect = partsInfos.stream().filter(p -> {

            List<HondaHpartAndHmodEntity> entities = groupByHpart.get(p.getHpartplblk());
            if (entities == null || entities.isEmpty()) {
                //conditionC
                if (conditionCIsEmpty) {
                    return false;
                }
                List<HondaConditionCEntity> hondaConditionCEntities = hmod2Condition.get(p.getNpl() + "_" + p.getNplblk());
                if (hondaConditionCEntities == null) {
                    return false;
                }
                List<Integer> hmodtyps3 = hondaConditionCEntities.stream()
                        .map(HondaConditionCEntity::getHmodtyp).collect(Collectors.toList());
                return !hmodtyps3.isEmpty();
            }
            List<Integer> hmodtyps1 = groupByHpart.get(p.getHpartplblk()).stream()
                    .filter(h -> analyseResult.getHmodtyps().contains(h.getHmodtyp()))
                    .map(HondaHpartAndHmodEntity::getHmodtyp).collect(Collectors.toList());
            return hmodtyps1 != null && !hmodtyps1.isEmpty();
        }).collect(Collectors.toList());
        return collect;
    }

    @Override
    public Map<String, List<HondaPartsInfoDTO>> filterPartsInfoByVinAnalyseResult(Map<String, List<HondaPartsInfoDTO>> partsInfoGroupByPartsName, HondaVinInfoDTO analyseResult) {
        List<HondaSpecialNameEntity> hondaSpecialNameEntities = hondaPartDAO.querySpecialName(partsInfoGroupByPartsName.keySet());
        Map<String, Integer> name2Stamp = hondaSpecialNameEntities.stream()
                .collect(Collectors.toMap(HondaSpecialNameEntity::getBtrPartsName, HondaSpecialNameEntity::getStamp));
        partsInfoGroupByPartsName.forEach((key, infos) -> {
            if (infos.isEmpty()) {
                return;
            }

            List<HondaPartsInfoDTO> result = infos.stream().filter(i -> {
                String xpartrmrk = i.getXpartrmrk();
                return StringUtils.isNotBlank(xpartrmrk) && xpartrmrk.contains("chin");
            }).collect(Collectors.toList());
            if (!result.isEmpty()) {
                infos = result;
            }

            Integer stamp = name2Stamp.get(key);
            if (stamp == null) {
                return;
            }
            if (stamp == 1) {
                List<HondaPartsInfoDTO> resultList = Lists.newArrayList();
                Map<String, List<HondaPartsInfoDTO>> partsCode2Info = infos.stream().collect(Collectors.groupingBy(HondaPartsInfoDTO::getNpartgenu));
                List<HondaPartsColorInfoEntity> colors = hondaPartDAO.queryColorInfo(partsCode2Info.keySet(), analyseResult.getDisk());

                List<HondaPartsColorInfoEntity> colorRemain = new ArrayList<>();
                for (HondaPartsColorInfoEntity c : colors) {
                    for (HondaPartsInfoDTO p : infos) {
                        if (c.getNpl().equals(p.getNpl())) {
                            colorRemain.add(c);
                        }
                    }
                }
                colors = colorRemain;
                List<HondaPartsColorInfoEntity> specialColor = colors.stream().filter(c -> c.getXhecol1().toUpperCase().contains("BLACK")).collect(Collectors.toList());
                if (specialColor.isEmpty()) {
                    specialColor = colors.stream().filter(c -> c.getXhecol1().toUpperCase().contains("WHITE")).collect(Collectors.toList());
                } else if (specialColor.size() > 1) {
                    List<HondaPartsColorInfoEntity> filterIfMany = specialColor.stream().filter(c -> {
                        String chescol = c.getChescol();
                        return chescol.contains("B92P") || chescol.contains("NH730") || chescol.contains("NH731P");
                    }).collect(Collectors.toList());
                    if (!filterIfMany.isEmpty()) {
                        specialColor = filterIfMany;
                    }
                }
                specialColor.forEach(b -> resultList.addAll(partsCode2Info.get(b.getNpartgenu())));
                if (!resultList.isEmpty()) {
                    infos = resultList;
                }
            } else if (stamp == 2) {
                if (!infos.isEmpty()) {
                    infos.sort((o1, o2) -> o1.getNpartgenu().compareToIgnoreCase(o2.getNpartgenu()));
                    infos = Lists.newArrayList(infos.get(0));
                }
            }

            partsInfoGroupByPartsName.put(key, infos);
        });
        return partsInfoGroupByPartsName;
    }


    private boolean filterByStrtEnd(HondaPartsInfoDTO partsInfo, HondaVinInfoDTO analyseResult) {
        if (StringUtils.isBlank(analyseResult.getSuffix8())) {
            return true;
        }
        String suffix8 = analyseResult.getSuffix8();
        return suffix8.compareTo(partsInfo.getNserepcend()) <= 0 && analyseResult.getSuffix8().compareTo(partsInfo.getNserepcstrt()) >= 0;
    }


    @Override
    public Map<String, List<EPCPartsImageDTO>> queryPartsImage(Map<String, List<HondaPartsInfoDTO>> partsInfoGroup, HondaVinInfoDTO analyseResult) {
        if (analyseResult == null) {
            return Collections.emptyMap();
        }
        List<HondaImageDTO> hondaImageDTOS = hondaImageDAO.queryImageNameCode();
        if (CollectionUtils.isEmpty(hondaImageDTOS)) {
            log.error("honda_pblokt数据缺失");
            return Collections.emptyMap();
        }
        Map<String, String> codeToNameMap = hondaImageDTOS.stream().collect(Collectors.toMap(HondaImageDTO::getImageName, HondaImageDTO::getImageCode));
        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(e -> e,
                p -> {
                    List<HondaPartsInfoDTO> hondaPartsInfoDTOS = partsInfoGroup.get(p);
                    if (CollectionUtils.isEmpty(hondaPartsInfoDTOS)) {
                        return Collections.emptyList();
                    }
                    List<HondaPartsImageDTO> imageInfos = hondaPartsImageDAO.findImageInfos(hondaPartsInfoDTOS, analyseResult.getDisk());
                    if (CollectionUtils.isEmpty(imageInfos)) {
                        return Collections.emptyList();
                    }
                    Map<String, List<HondaPartsImageDTO>> imgMap = imageInfos.stream().collect(groupingBy(z -> z.getNpl() + "/" + z.getIllustrationNumber() + "/" + z.getPartReferenceNumber()));
                    return hondaPartsInfoDTOS.stream().filter(partsInfo -> {
                        List<HondaPartsImageDTO> imageInfoDTOs = imgMap.get(partsInfo.getNpl() + "/" + partsInfo.getNplblk() + "/" + partsInfo.getNplpartref());
                        return CollectionUtils.isNotEmpty(imageInfoDTOs);
                    }).map(partsInfo -> {
                        List<HondaPartsImageDTO> imageInfoDTOs = imgMap.get(partsInfo.getNpl() + "/" + partsInfo.getNplblk() + "/" + partsInfo.getNplpartref());
                        List<EPCPartsImagePositionDTO> positionDTOS = Lists.newArrayList();
                        for (HondaPartsImageDTO imageInfo : imageInfoDTOs) {
                            EPCPartsImagePositionDTO positionDTO = new EPCPartsImagePositionDTO();
                            positionDTO.setPartsCode(partsInfo.getPartsCode());
                            positionDTO.setPosition(imageInfo.getPartReferenceNumber().toString());
                            EPCImagePointDTO bottomRight = new EPCImagePointDTO();
                            bottomRight.setX(imageInfo.getBottomRightX());
                            bottomRight.setY(imageInfo.getBottomRightY());
                            EPCImagePointDTO topLeft = new EPCImagePointDTO();
                            topLeft.setX(imageInfo.getTopLeftX());
                            topLeft.setY(imageInfo.getTopLeftY());
                            positionDTO.setBottomRight(bottomRight);
                            positionDTO.setTopLeft(topLeft);
                            positionDTOS.add(positionDTO);
                        }
                        String imgPath = getImageURL(analyseResult, partsInfo.getNplblk());
                        EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
                        epcPartsImageDTO.setImageName(partsInfo.getNplblk());
                        epcPartsImageDTO.setImageCode(codeToNameMap.get(partsInfo.getNplblk()));
                        epcPartsImageDTO.setImageUrl(imgPath);
                        epcPartsImageDTO.setEpcPartsImagePositionDTOs(positionDTOS);
                        return epcPartsImageDTO;
                    }).distinct().collect(Collectors.toList());
                }));
    }

    @Override
    public Map<String, String> queryColorRemark(Map<String, List<HondaPartsInfoDTO>> partsInfoGroup, HondaVinInfoDTO analyseResult) {
        if (analyseResult == null) {
            return Collections.emptyMap();
        }
        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(e -> e,
                p -> {
                    List<HondaPartsInfoDTO> hondaPartsInfoDTOS = partsInfoGroup.get(p);
                    Set<String> partsCodes = hondaPartsInfoDTOS.stream().map(HondaPartsInfoDTO::getPartsCode).collect(Collectors.toSet());
                    return fillColorRemark(partsCodes, analyseResult.getDisk());
                }));
    }

    @Override
    public List<HondaPartsMappingDTO> queryPartsMappingByPartsInfoAndAnalyseResult(Map<String, List<HondaPartsInfoDTO>> partsInfoGroup, HondaVinInfoDTO analyseResult) {
        Set<String> partsCodes = partsInfoGroup.values().stream()
                .flatMap(l -> l.stream().map(HondaPartsInfoDTO::getPartsCode))
                .collect(Collectors.toSet());
        return StreamKit.partitionStream(partsCodes, SEGMENT_QUERY_SIZE).flatMap(l ->
                hondaPartsMappingDAO.getByPartsCode(l).stream()
        ).collect(Collectors.toList());
    }

    @Override
    public List<EPCPartsDTO> getEPCPartsDTOListFromBtrPartsByPartsName(Set<String> btrPartsNames, HondaVinInfoDTO hondaVinInfoDTO) {
        if (CollectionUtils.isEmpty(btrPartsNames) || hondaVinInfoDTO == null) {
            return Collections.emptyList();
        }

        //非油类自编码
        Set<HondaBtrPartsDTO> btrPartsInfo = queryBtrParts(btrPartsNames, hondaVinInfoDTO);
        //油类自编码
        Set<HondaBtrPartsDTO> btrPartsInfoByOil = queryBtrOilParts(btrPartsNames, hondaVinInfoDTO);

        Set<HondaBtrPartsDTO> resultBtrPartsInfo = Sets.union(btrPartsInfo, btrPartsInfoByOil);
        if (CollectionUtils.isEmpty(resultBtrPartsInfo)) {
            return Collections.emptyList();
        }

        return resultBtrPartsInfo.stream().map(p -> {
            EPCPartsDTO epcPartsDTO = new EPCPartsDTO();
            epcPartsDTO.setBtrPartsName(p.getBtrPartsName());
            epcPartsDTO.setPartsCode(p.getPartsCode());
            if (p.isHasImg()) {
                EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
                epcPartsImageDTO.setImageName(p.getPartsCode());
                String imageUrl = "oil/" + p.getPartsCode() + ".png";
                String imgPath = appConfigLoader.getEPCPartsImageFullUrl(EPCAnalyserEnum.HONDA, imageUrl);
                epcPartsImageDTO.setImageUrl(imgPath);
                epcPartsDTO.setEpcPartsImageDTOs(Lists.newArrayList(epcPartsImageDTO));
            }
            return epcPartsDTO;
        }).collect(Collectors.toList());
    }

    private Set<HondaBtrPartsDTO> queryBtrOilParts(Set<String> btrPartsNames, HondaVinInfoDTO analyseResult) {
        //这一块是特殊规则, 数据编辑要求变速箱油走旧方法
        Set<String> remainBtrPartsName = btrPartsNames;
        Set<HondaBtrPartsDTO> hondaBtrPartsDTOS = hondaPartsCarTypeDAO.oldWayToFindPartsCodes(analyseResult.getBtrId(), btrPartsNames);
        if (CollectionUtils.isNotEmpty(hondaBtrPartsDTOS)) {
            remainBtrPartsName = btrPartsNames.stream().filter(p -> hondaBtrPartsDTOS.stream().noneMatch(l -> p.equals(l.getBtrPartsName()))).collect(Collectors.toSet());
        }

        if (CollectionUtils.isEmpty(remainBtrPartsName)) {
            return hondaBtrPartsDTOS;
        }
        Set<HondaBtrPartsDTO> hondaBtrPartsByOil = queryOilRelation(btrPartsNames, analyseResult);

        return Sets.union(hondaBtrPartsDTOS, hondaBtrPartsByOil);
    }

    private Set<HondaBtrPartsDTO> queryOilRelation(Set<String> btrPartsNames, HondaVinInfoDTO analyseResult) {
        List<HondaBtrPartsDTO> oilMappings = hondaBtrPartsDAO.queryOilRelationByBtrPartsNames(btrPartsNames, analyseResult.getBrand());
        if (CollectionUtils.isEmpty(oilMappings)) {
            return Collections.emptySet();
        }

        Set<HondaBtrPartsDTO> oilBtrParts = oilMappings.stream().peek(p -> {
            if ("变速箱油".equals(p.getBtrPartsName())) {
                String cmodnamepc = analyseResult.getCmodnamepc();
                //变速箱AT是08268P9904ZJ1,MT是08267P6601YJ1
                //08269P9904ZJ3（CVT）：飞度，杰德，思铂睿；08269P99Z4FJ1（CVT）剩余其他车系
                String ctrsmtyp = analyseResult.getCtrsmtyp();
                String dmodyr = analyseResult.getDmodyr();
                if (StringUtils.isBlank(ctrsmtyp)) {
                    return;
                }

                if (StringUtils.isNotBlank(cmodnamepc) && ouges.contains(cmodnamepc) && !"CDX".equals(cmodnamepc)) {
                    p.setPartsCode("08268A9901ZJ1");
                } else if (ctrsmtyp.contains("AT") && !analyseResult.getNfrmpf().contains("TG")) {
                    p.setPartsCode("08268P9904ZJ1");
                } else if (ctrsmtyp.contains("MT")) {
                    p.setPartsCode("08267P6601YJ1");
                } else if (ctrsmtyp.contains("CVT")) {
                    if (StringUtils.isNotBlank(cmodnamepc) &&
                            (cmodnamepc.startsWith("FIT") && Integer.valueOf(dmodyr) < 2015) || cmodnamepc.startsWith("JADE") && Integer.valueOf(dmodyr) < 2015 || cmodnamepc.startsWith("SPIRIOR")) {
                        p.setPartsCode("08269P9904ZJ3");
                    } else {
                        if (cmodnamepc.startsWith("JADE")) {
                            if (Integer.valueOf(dmodyr) < 2015) {
                                p.setPartsCode("08269P99Z4FJ1");
                            }
                        } else {
                            p.setPartsCode("08269P99Z4FJ1");
                        }
                    }
                }
            }
        }).collect(Collectors.toSet());
        oilBtrParts.removeIf(p ->
                !p.isHasImg() && oilBtrParts.stream().anyMatch(l -> l.isHasImg() && l.getPartsCode().equals(p.getPartsCode()))
        );
        return oilBtrParts;
    }

    private Set<HondaBtrPartsDTO> queryBtrParts(Set<String> btrPartsNames, HondaVinInfoDTO analyseResult) {
        if (StringUtils.isBlank(analyseResult.getBrand())) {
            return Collections.emptySet();
        }
        Set<HondaBtrPartsDTO> hondaBtrPartsDTOS = hondaBtrPartsDAO.queryBtrPartsNameByBrand(analyseResult.getBrand(), btrPartsNames);
        if (CollectionUtils.isEmpty(hondaBtrPartsDTOS)) {
            return Collections.emptySet();
        }
        Set<String> basicPartsName = hondaBtrPartsDTOS.stream().map(HondaBtrPartsDTO::getBasicPartsName).collect(Collectors.toSet());

        List<HondaPartsMappingDTO> partsMappings = queryPartsMappingsForCall(basicPartsName, analyseResult);
        List<HondaPartsInfoDTO> partsInfo = queryPartsInfoByPartsMappingForCall(partsMappings, analyseResult);
        partsInfo = filterPartsInfoByVinAnalyseResultForCall(partsInfo, analyseResult);
        Map<String, List<HondaPartsInfoDTO>> partsInfoGroupByPartsName = getPartsInfoGroupByPartsNameForCall(partsInfo, partsMappings);
        partsInfoGroupByPartsName = filterPartsInfoByVinAnalyseResultForCall(partsInfoGroupByPartsName, analyseResult);
        List<EPCPartsDTO> epcPartsList = getEPCPartsDTOListForCall(partsInfoGroupByPartsName, analyseResult, null);
        if (CollectionUtils.isEmpty(epcPartsList)) {
            return Collections.emptySet();
        }

        Map<String, List<EPCPartsDTO>> partsMap = epcPartsList.stream().collect(Collectors.groupingBy(EPCPartsDTO::getBtrPartsName));
        return hondaBtrPartsDTOS.stream()
                .filter(p -> CollectionUtils.isNotEmpty(partsMap.get(p.getBasicPartsName())))
                .flatMap(p -> {
                    List<EPCPartsDTO> epcPartsDTOS = partsMap.get(p.getBasicPartsName());
                    return epcPartsDTOS.stream().map(l ->
                            HondaBtrPartsDTO.builder()
                                    .btrPartsName(p.getBtrPartsName())
                                    .partsCode(l.getPartsCode() + p.getDistinguishingMarks() + p.getSuffix())
                                    .build()
                    );
                }).collect(Collectors.toSet());
    }

    @Override
    public List<EPCPartsDTO> getEPCPartsDTOListFromBtrPartsByPartsCode(Set<String> partsCodes, HondaVinInfoDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsCodes) || analyseResult == null) {
            return Collections.emptyList();
        }
        List<EPCPartsDTO> epcPartsDTOs = Lists.newArrayList();
        List<HondaBtrPartsDTO> hondaPartsMappingDTOS = hondaBtrPartsDAO.queryOilRelationByBtrPartsCodes(partsCodes);
        if (CollectionUtils.isNotEmpty(hondaPartsMappingDTOS)) {
            Map<String, List<HondaBtrPartsDTO>> codeMaps = hondaPartsMappingDTOS.stream().collect(groupingBy(HondaBtrPartsDTO::getPartsCode));
            for (String partsCode : codeMaps.keySet()) {
                List<HondaBtrPartsDTO> partsMappingDTOS = codeMaps.get(partsCode);
                if (CollectionUtils.isEmpty(partsMappingDTOS)) {
                    continue;
                }
                fillEpcPartsDTOs(null, partsMappingDTOS.get(0), epcPartsDTOs);
            }
        }
        List<String> partsCodeList = hondaPartsCarTypeDAO.oldWayToFilterByPartsCodes(analyseResult.getBtrId(), partsCodes);
        if (CollectionUtils.isNotEmpty(partsCodeList)) {
            for (String partsCode : partsCodeList) {
                EPCPartsDTO epcPartsDTO = new EPCPartsDTO();
                epcPartsDTO.setPartsCode(partsCode);
                epcPartsDTOs.add(epcPartsDTO);
            }
        }
        return epcPartsDTOs;
    }

    @Override
    public List<EPCPartsCodeResultDTO> queryBtrPartsCode(Set<String> partsCodes) {
        if (CollectionUtils.isEmpty(partsCodes)) {
            return Collections.emptyList();
        }
        List<HondaBtrPartsDTO> hondaBtrPartsDTOS = hondaBtrPartsDAO.queryOilRelationByBtrPartsCodes(partsCodes);
        List<String> selfPartsCodes = hondaPartsCarTypeDAO.querySelfPartsCodes(partsCodes);

        Set<String> finalPartsCode = Sets.newHashSet();

        if (CollectionUtils.isNotEmpty(hondaBtrPartsDTOS)) {
            hondaBtrPartsDTOS.stream()
                    .filter(l -> StringUtils.isNotBlank(l.getPartsCode()))
                    .forEach(l -> finalPartsCode.add(l.getPartsCode()));
        }

        if (CollectionUtils.isNotEmpty(selfPartsCodes)) {
            finalPartsCode.addAll(selfPartsCodes);
        }

        if (CollectionUtils.isEmpty(finalPartsCode)) {
            return Collections.emptyList();
        }

        return finalPartsCode.stream().map(l -> EPCPartsCodeResultDTO.builder().partsCode(l).build()).distinct().collect(Collectors.toList());
    }

    @Override
    public Map<String, Set<String>> queryReplaceCodes(Set<String> partsCodes, HondaVinInfoDTO analyseResult) {

        if (analyseResult == null) {
            return Collections.emptyMap();
        }
        Set<HondaVinMultiInfoDTO> vinMultiInfoDTOs = analyseResult.getVinMultiInfoDTO();
        if (CollectionUtils.isEmpty(vinMultiInfoDTOs)) {
            return Collections.emptyMap();
        }
        Set<Integer> hmodtyps = vinMultiInfoDTOs.stream().map(HondaVinMultiInfoDTO::getHmodtyp).collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(hmodtyps)) {
            return Collections.emptyMap();
        }

        partsCodes = filterByAnalyser(partsCodes, analyseResult);
        if (CollectionUtils.isEmpty(partsCodes)) {
            return Collections.emptyMap();
        }

        List<HondaRepPartsDTO> repPartsInfos = hondaPartDAO.findRepPartsInfos(analyseResult.getDisk(), analyseResult.getNpl(), partsCodes, hmodtyps);
        if (CollectionUtils.isEmpty(repPartsInfos)) {
            return Collections.emptyMap();
        }
        return repPartsInfos.stream()
                .peek(d -> d.setLegal(filterByAnalyser(Sets.newHashSet(d.getRepPartsCode()), analyseResult).contains(d.getRepPartsCode())))
                .filter(HondaRepPartsDTO::isLegal)
                .collect(Collectors.groupingBy(HondaRepPartsDTO::getPartsCode, Collectors.mapping(HondaRepPartsDTO::getRepPartsCode, Collectors.toSet())));
    }

    @Override
    public Map<String, String> queryEpcPartsName(Map<String, List<HondaPartsInfoDTO>> partsInfoGroup, HondaVinInfoDTO analyseResult) {
        Set<String> partsCode = partsInfoGroup.values().stream()
                .flatMap(Collection::stream)
                .map(HondaPartsInfoDTO::getPartsCode)
                .collect(Collectors.toSet());

        if (CollectionUtils.isEmpty(partsCode)) {
            return Collections.emptyMap();
        }
        Map<String, List<HondaPartsDescDTO>> partsCNGroupByPartsCode = hondaPartDAO.findPartsDescCN(partsCode)
                .stream()
                .collect(Collectors.groupingBy(HondaPartsDescDTO::getPartsCode));

        //中文epc配件名称
        Map<String, String> result = Maps.newHashMap();
        for (String key : partsInfoGroup.keySet()) {
            List<HondaPartsDescDTO> hondaPartsDescDTOS = partsCNGroupByPartsCode.get(key);
            result.put(key, CollectionUtils.isEmpty(hondaPartsDescDTOS) ? null : hondaPartsDescDTOS.get(0).getEpcPartsNameCN());
        }

        Set<String> emptyPartsCode = result.entrySet().stream()
                .filter(e -> StringUtils.isEmpty(e.getValue()))
                .map(Map.Entry::getKey)
                .collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(emptyPartsCode)) {
            return result;
        }

        Map<String, List<HondaPartsDescDTO>> partsENGroupByPartsCode = hondaPartDAO.findPartsDesc(emptyPartsCode).stream().collect(Collectors.groupingBy(HondaPartsDescDTO::getPartsCode));

        for (String key : emptyPartsCode) {
            List<HondaPartsDescDTO> hondaPartsDescDTOS = partsENGroupByPartsCode.get(key);

            if (CollectionUtils.isEmpty(hondaPartsDescDTOS)) {
                continue;
            }

            Optional<HondaPartsDescDTO> equalsDisk = hondaPartsDescDTOS.stream()
                    .filter(d -> d.getDisk().equals(analyseResult.getDisk()))
                    .findFirst();
            result.put(key, equalsDisk.isPresent() ? equalsDisk.get().getEpcPartsName() : hondaPartsDescDTOS.get(0).getEpcPartsName());
        }
        return result;
    }

    @Override
    protected Map<String, Set<String>> queryFeatures(Map<String, List<HondaPartsInfoDTO>> partsInfoGroup, HondaVinInfoDTO analyseResult) {
        Set<String> partsCode = partsInfoGroup.values().stream()
                .flatMap(Collection::stream)
                .map(HondaPartsInfoDTO::getPartsCode)
                .collect(Collectors.toSet());

        if (CollectionUtils.isEmpty(partsCode)) {
            return Collections.emptyMap();
        }
        List<HondaPartsFeaturesDTO> hondaPartsFeaturesDTOS = hondaPartDAO.queryPartsFeatures(partsCode).stream().filter(d -> StringUtils.isNoneBlank(d.getFeatures())).collect(Collectors.toList());
        Map<String, Set<String>> result = Maps.newHashMap();
        hondaPartsFeaturesDTOS.forEach(p -> {
            Set<String> features = result.computeIfAbsent(p.getPartsCode(), k -> Sets.newHashSet());
            features.add(p.getFeatures());
        });
        return result;
    }

    @Override
    protected Map<String, Set<String>> queryMaterial(Map<String, List<HondaPartsInfoDTO>> partsInfoGroup, HondaVinInfoDTO analyseResult) {
        Set<String> partsCode = partsInfoGroup.values().stream()
                .flatMap(Collection::stream)
                .map(HondaPartsInfoDTO::getPartsCode)
                .collect(Collectors.toSet());

        if (CollectionUtils.isEmpty(partsCode)) {
            return Collections.emptyMap();
        }
        return hondaPartDAO.queryPartsFeatures(partsCode).stream().filter(d -> StringUtils.isNotBlank(d.getQuality()))
                .collect(Collectors.toMap(HondaPartsFeaturesDTO::getPartsCode, d -> Sets.newHashSet(d.getQuality())));
    }

    @Override
    public Map<String, String> queryInstallNum(Map<String, List<HondaPartsInfoDTO>> partsInfoGroup, HondaVinInfoDTO analyseResult) {
        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(e -> e, p -> {
            List<HondaPartsInfoDTO> hondaPartsInfoDTOS = partsInfoGroup.get(p);
            if (CollectionUtils.isEmpty(hondaPartsInfoDTOS)) {
                return StringUtils.EMPTY;
            }
            Set<String> installNumber = hondaPartsInfoDTOS.stream()
                    .filter(z -> StringUtils.isNotBlank(z.getXordergun()))
                    .map(HondaPartsInfoDTO::getXordergun)
                    .collect(Collectors.toSet());
            return combineString(installNumber);
        }));
    }

    @Override
    public Map<String, List<BasePartsImageInfoDTO>> queryEPCImageInfo(Set<String> sortNames, HondaVinInfoDTO analyseResult) {
        if (CollectionUtils.isEmpty(sortNames) || analyseResult == null || StringUtils.isBlank(analyseResult.getNpl()) ||
                analyseResult.getHmodtyp() == null || analyseResult.getHmodtyp() <= 0 || StringUtils.isBlank(analyseResult.getDisk()) ||
                CollectionUtils.isEmpty(analyseResult.getNplblks())) {
            return Collections.emptyMap();
        }
        List<HondaImageDTO> hondaImageDTOS = hondaImageDAO.queryImageCodeBySortName(analyseResult.getNpl(), sortNames);
        if (CollectionUtils.isEmpty(hondaImageDTOS)) {
            log.info("分类:{},没有相应的图组映射", sortNames);
            return Collections.emptyMap();
        }
        Set<String> imageCodes = hondaImageDTOS.stream().map(HondaImageDTO::getImageCode).collect(Collectors.toSet());
        //通过车型信息过滤图号
        List<HondaImageDTO> imageCodeNameDTO = hondaImageDAO.queryImageNameByNpl(analyseResult.getNpl(), imageCodes);
        if (CollectionUtils.isEmpty(imageCodeNameDTO)) {
            return Collections.emptyMap();
        }

        Map<String, String> codeToNameMap = imageCodeNameDTO.stream().collect(Collectors.toMap(HondaImageDTO::getImageCode, HondaImageDTO::getImageName));

        hondaImageDTOS.stream().filter(p -> StringUtils.isNotBlank(codeToNameMap.get(p.getImageCode()))).forEach(p -> {
            String imageName = codeToNameMap.get(p.getImageCode());
            p.setImageName(imageName);
            String imageUrl = analyseResult.getNpl() + "/IMGE/" + imageName + ".png";
            String imgPath = appConfigLoader.getEPCPartsImageFullUrl(EPCAnalyserEnum.HONDA, imageUrl);
            p.setImageUrl(imgPath);
        });

        Map<String, List<HondaImageDTO>> mapBySortName = hondaImageDTOS.stream().filter(p -> StringUtils.isNotBlank(p.getImageName()) && analyseResult.getNplblks().contains(p.getImageName())).collect(Collectors.groupingBy(HondaImageDTO::getSortName));
        return sortNames.stream().collect(Collectors.toMap(e -> e, sortName -> {
            List<HondaImageDTO> imageDTOS = mapBySortName.get(sortName);
            if (CollectionUtils.isEmpty(imageDTOS)) {
                return Collections.emptyList();
            }
            return imageDTOS.stream().distinct().collect(Collectors.toList());
        }));
    }

    @Override
    protected List<HondaPartsInfoDTO> queryPartsInfoByImageCode(Set<String> picNos, HondaVinInfoDTO analyseResult) {

        if (CollectionUtils.isEmpty(picNos) || analyseResult == null || StringUtils.isBlank(analyseResult.getNpl()) ||
                analyseResult.getHmodtyp() == null || analyseResult.getHmodtyp() <= 0 || StringUtils.isBlank(analyseResult.getDisk()) ||
                CollectionUtils.isEmpty(analyseResult.getNplblks()) || StringUtils.isBlank(analyseResult.getDisk())) {
            return Collections.emptyList();
        }

        //通过车型信息过滤图号
        List<HondaImageDTO> imageCodeNameDTO = hondaImageDAO.queryImageNameByNpl(analyseResult.getNpl(), picNos);
        if (CollectionUtils.isEmpty(imageCodeNameDTO)) {
            return Collections.emptyList();
        }

        Set<String> imageNames = imageCodeNameDTO.stream().map(HondaImageDTO::getImageName).collect(Collectors.toSet());
        imageNames.removeIf(p -> !analyseResult.getNplblks().contains(p));
        if (CollectionUtils.isEmpty(imageNames)) {
            return Collections.emptyList();
        }

        //查询图号下的所有编码
        List<HondaPartsInfoDTO> partsInfo = hondaPartDAO.findPartsInfoByNplAndNplBlk(analyseResult.getNpl(), analyseResult.getDisk(), imageNames);
        return filterPartsInfoByVinAnalyseResult(partsInfo, analyseResult);
    }

    @Override
    public List<PartsAssemblyDTO> queryPartsAssembly(Set<String> imageCodes, Set<String> partsNames, HondaVinInfoDTO analyseResult) {
        if (CollectionUtils.isEmpty(imageCodes) || analyseResult == null || CollectionUtils.isEmpty(analyseResult.getNplblks())) {
            return Collections.emptyList();
        }

        //通过车型信息过滤图号
        List<HondaImageDTO> imageCodeNameDTO = hondaImageDAO.queryImageNameByNpl(analyseResult.getNpl(), imageCodes);
        if (CollectionUtils.isEmpty(imageCodeNameDTO)) {
            return Collections.emptyList();
        }

        Set<String> imageNames = imageCodeNameDTO.stream().map(HondaImageDTO::getImageName).collect(Collectors.toSet());
        imageNames.removeIf(p -> !analyseResult.getNplblks().contains(p));
        if (CollectionUtils.isEmpty(imageNames)) {
            return Collections.emptyList();
        }

        Set<String> filteredImageCodes = imageCodeNameDTO.stream().filter(p -> imageNames.contains(p.getImageName())).map(HondaImageDTO::getImageCode).collect(Collectors.toSet());


        String npl = analyseResult.getNpl();
        Set<String> groupNames = hondaAssemblyDAO.queryGroupNames(npl, filteredImageCodes);

        if (CollectionUtils.isEmpty(groupNames)) {
            log.warn("没有找到groupNames - VIN：{}，imageCodes:{} ", analyseResult.getVinCode(), String.join(",", imageCodes));
            return Collections.emptyList();
        }

        List<HondaAssemblyDTO> hondaAssemblyDTOs = StreamKit.partitionStream(filteredImageCodes, SEGMENT_QUERY_SIZE)
                .flatMap(imageCode -> StreamKit.partitionStream(partsNames, SEGMENT_QUERY_SIZE)
                        .flatMap(partsName -> StreamKit.partitionStream(groupNames, SEGMENT_QUERY_SIZE)
                                .flatMap(groupName -> hondaAssemblyDAO.queryAssembly(imageCode, partsName, groupName).stream())))
                .collect(Collectors.toList());

        return hondaAssemblyDTOs.stream()
                .map(d -> PartsAssemblyDTO.builder()
                        .imageCode(d.getImageCode())
                        .assPartsName(d.getAssPartsName())
                        .nonAssPartsName(d.getNonAssPartsName())
                        .build())
                .distinct()
                .collect(Collectors.toList());
    }

    private void fillEpcPartsDTOs(String btrPartsName, HondaBtrPartsDTO hondaBtrPartsDTO, List<EPCPartsDTO> epcPartsDTOs) {
        EPCPartsDTO epcPartsDTO = new EPCPartsDTO();
        epcPartsDTO.setBtrPartsName(btrPartsName);
        String partsCode = hondaBtrPartsDTO.getPartsCode();
        epcPartsDTO.setPartsCode(partsCode);
        if (hondaBtrPartsDTO.isHasImg()) {
            EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
            epcPartsImageDTO.setImageName(partsCode);
            String imageUrl = "oil/" + partsCode + ".png";
            String imgPath = appConfigLoader.getEPCPartsImageFullUrl(EPCAnalyserEnum.HONDA, imageUrl);
            epcPartsImageDTO.setImageUrl(imgPath);
            epcPartsDTO.setEpcPartsImageDTOs(Lists.newArrayList(epcPartsImageDTO));
        }
        epcPartsDTOs.add(epcPartsDTO);
    }

    private String combineString(Set<String> set) {
        String result = "";
        if (CollectionUtils.isNotEmpty(set)) {
            for (String remark : set) {
                if (StringUtils.isNotBlank(remark)) {
                    String clearNum = remark.replace("(", "").replace(")", "");
                    if (StringUtils.isNumeric(clearNum)) {
                        remark = String.valueOf(Integer.parseInt(clearNum));
                    }
                    result = remark + "/";
                }
            }
            if (StringUtils.isNotBlank(result)) {
                result = result.substring(0, result.length() - 1);
            }
        }
        return result;
    }

    private Set<String> filterByAnalyser(Set<String> partsCodes, HondaVinInfoDTO analyseResult) {
        if (analyseResult == null) {
            return Collections.emptySet();
        }
        List<HondaPartsInfoDTO> queryPartsInfoByPartsCode = queryPartsInfoByPartsCode(partsCodes, analyseResult);
        if (queryPartsInfoByPartsCode.isEmpty()) {
            return Collections.emptySet();
        } else {
            List<HondaPartsInfoDTO> hondaPartsInfoDTOS = filterPartsInfoByVinAnalyseResult(queryPartsInfoByPartsCode, analyseResult);
            return hondaPartsInfoDTOS.stream().map(HondaPartsInfoDTO::getPartsCode).collect(Collectors.toSet());
        }
    }

    private void filterByEngine(List<HondaPartsInfoDTO> partsInfos) {
        List<HondaPartsInfoDTO> removeDTOs = partsInfos.stream().filter(p -> "E".equals(p.getCsernumcont())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(removeDTOs)) {
            return;
        }
        Map<String, List<HondaPartsInfoDTO>> groupByNplBlkRef = removeDTOs.stream().collect(groupingBy(p -> p.getNpl() + p.getNplblk() + p.getNplpartref()));
        for (String nplBlkRef : groupByNplBlkRef.keySet()) {
            List<HondaPartsInfoDTO> hondaPartsInfoDTOs = groupByNplBlkRef.get(nplBlkRef);
            if (CollectionUtils.isNotEmpty(hondaPartsInfoDTOs) && hondaPartsInfoDTOs.size() > 1) {
                Comparator<HondaPartsInfoDTO> comparator = (h1, h2) -> h1.getNserepcstrt().compareTo(h2.getNserepcend());
                hondaPartsInfoDTOs.sort(comparator.reversed());
                hondaPartsInfoDTOs.remove(hondaPartsInfoDTOs.get(0));
                partsInfos.removeAll(hondaPartsInfoDTOs);
            }
        }
    }

    private void filterByHpartplblk(List<HondaPartsInfoDTO> partsInfos, HondaVinInfoDTO analyseResult) {
        Set<HondaVinMultiInfoDTO> vinMultiInfoDTOs = analyseResult.getVinMultiInfoDTO();
        Set<Integer> hpartplblks = partsInfos.stream().map(HondaPartsInfoDTO::getHpartplblk).collect(Collectors.toSet());
        Set<Integer> hasRelationPart = hondaPartDAO.findPartsRelation(hpartplblks, analyseResult.getDisk());
        hpartplblks.removeIf(hasRelationPart::contains);
        if (CollectionUtils.isNotEmpty(hasRelationPart)) {
            Set<Integer> hmodtyps = vinMultiInfoDTOs.stream().map(HondaVinMultiInfoDTO::getHmodtyp).collect(Collectors.toSet());
            Set<Integer> fileredPartBlks = hondaPartDAO.findPartsByHmodTyps(hasRelationPart, hmodtyps, analyseResult.getDisk());
            hpartplblks.addAll(fileredPartBlks);
        }
        partsInfos.removeIf(p -> !hpartplblks.contains(p.getHpartplblk()));
    }

    private void filterByVinSuffix8(List<HondaPartsInfoDTO> partsInfos, HondaVinInfoDTO analyseResult) {
        String suffix8 = analyseResult.getVinCode().substring(9).toUpperCase();
        if (CollectionUtils.isNotEmpty(partsInfos)) {
            partsInfos.removeIf(p -> "F".equals(p.getCsernumcont()) && !(p.getNserepcstrt().compareTo(suffix8) <= 0 && p.getNserepcend().compareTo(suffix8) >= 0));
        }
    }

    private String fillColorRemark(Set<String> partsCodes, String disk) {
        List<HondaPartsInfoDTO> partsColor = hondaPartDAO.findPartsColor(partsCodes, disk);
        if (CollectionUtils.isEmpty(partsColor)) {
            return StringUtils.EMPTY;
        }
        List<String> colors = partsColor.stream().map(HondaPartsInfoDTO::getColorRemark).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(colors)) {
            return StringUtils.EMPTY;
        }
        return colors.get(0);
    }

    @Override
    protected List<EPCPartsImageDTO> queryEPCImageInfoByImageCode(Set<String> imageCodes, HondaVinInfoDTO analyseResult) {
        if (CollectionUtils.isEmpty(imageCodes) || analyseResult == null
                || StringUtils.isBlank(analyseResult.getDisk()) || StringUtils.isBlank(analyseResult.getNpl())
                || CollectionUtils.isEmpty(analyseResult.getNplblks())) {
            return Collections.emptyList();
        }

        //通过车型信息过滤图号
        List<HondaImageDTO> imageCodeNameDTO = hondaImageDAO.queryImageNameByNpl(analyseResult.getNpl(), imageCodes);
        if (CollectionUtils.isEmpty(imageCodeNameDTO)) {
            return Collections.emptyList();
        }

        Set<String> imageNames = imageCodeNameDTO.stream().map(HondaImageDTO::getImageName).collect(Collectors.toSet());
        imageNames.removeIf(p -> !analyseResult.getNplblks().contains(p));
        if (CollectionUtils.isEmpty(imageNames)) {
            return Collections.emptyList();
        }

        Map<String, String> nameToCodeMap = imageCodeNameDTO.stream().collect(Collectors.toMap(HondaImageDTO::getImageName, HondaImageDTO::getImageCode));

        List<HondaPartsImageDTO> partsImageDTOS = hondaPartsImageDAO.findImageInfoByImageNameAndNpl(imageNames, analyseResult.getNpl(), analyseResult.getDisk());
        if (CollectionUtils.isEmpty(partsImageDTOS)) {
            return Collections.emptyList();
        }

        Map<String, List<HondaPartsImageDTO>> positionMap = partsImageDTOS.stream().collect(Collectors.groupingBy(HondaPartsImageDTO::getIllustrationNumber));

        return imageNames.stream().filter(p -> StringUtils.isNotBlank(nameToCodeMap.get(p)))
                .filter(p -> CollectionUtils.isNotEmpty(positionMap.get(p)))
                .map(imageName -> {
                    EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
                    String imageCode = nameToCodeMap.get(imageName);
                    epcPartsImageDTO.setImageCode(imageCode);
                    String imgPath = getImageURL(analyseResult, imageName);
                    epcPartsImageDTO.setImageUrl(imgPath);
                    epcPartsImageDTO.setImageName(imageName);
                    List<EPCPartsImagePositionDTO> epcPartsImagePositionDTOs = transferToPositionDTO(positionMap.get(imageName));
                    epcPartsImageDTO.setEpcPartsImagePositionDTOs(epcPartsImagePositionDTOs);
                    return epcPartsImageDTO;
                }).collect(Collectors.toList());
    }

    private String getImageURL(HondaVinInfoDTO analyseResult, String imageName) {
        if (StringUtils.isBlank(analyseResult.getDisk()) || StringUtils.isBlank(analyseResult.getNpl())) {
            return null;
        }
        String imageUrl = analyseResult.getNpl() + "/IMGE/" + imageName;
        float ver = Float.parseFloat(analyseResult.getDisk()) / 2.0f;
        if (ver >= HondaDataVersionConstant.PRE_FIVE_VER_URL) {
            imageUrl = String.valueOf(Math.floor(ver)).replace(".0","") + "/" + imageUrl;
        }
        return appConfigLoader.getPartsPicBaseUrl() + imageUrl;
    }

    private List<EPCPartsImagePositionDTO> transferToPositionDTO(List<HondaPartsImageDTO> positions) {
        return positions.stream().map(postionDTO -> {
            EPCPartsImagePositionDTO epip = new EPCPartsImagePositionDTO();
            EPCImagePointDTO topLeft = new EPCImagePointDTO();
            EPCImagePointDTO bottomRight = new EPCImagePointDTO();
            topLeft.setX(postionDTO.getTopLeftX());
            topLeft.setY(postionDTO.getTopLeftY());
            bottomRight.setX(postionDTO.getBottomRightX());
            bottomRight.setY(postionDTO.getBottomRightY());
            epip.setPosition(String.valueOf(postionDTO.getPartReferenceNumber()));
            epip.setTopLeft(topLeft);
            epip.setBottomRight(bottomRight);
            return epip;
        }).collect(Collectors.toList());
    }

    @Override
    public Map<String, List<String>> queryEPCPartsCodeForDiyCartype(String btrId, Set<String> btrPartsNames) {
        if (CollectionUtils.isEmpty(btrPartsNames) || StringUtils.isBlank(btrId)) {
            return Collections.emptyMap();
        }
        Set<String> filterName = filterDiyBtrPartsName(btrPartsNames);
        if (CollectionUtils.isEmpty(btrPartsNames)) {
            return Collections.emptyMap();
        }
        List<String> vinCodes = hondaBtrPartsDAO.queryDiyCarByBtrid(btrId);
        Map<String, Set<String>> result = vinCodes.stream()
                .flatMap(vin -> epcQueryService.queryEPCPartsByBtrPartsName(vin, filterName).stream())
                .collect(groupingBy(EPCPartsDTO::getBtrPartsName, Collectors.mapping(EPCPartsDTO::getPartsCode, Collectors.toSet())));
        result.entrySet().removeIf(d -> d.getValue().size() > 1);
        return result.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey,
                        d -> Lists.newArrayList(d.getValue()),
                        (v1, v2) -> v1));
    }

    private Set<String> filterDiyBtrPartsName(Set<String> btrPartsNames) {
        return StreamKit.partitionStream(btrPartsNames, 30)
                .flatMap(l -> hondaBtrPartsDAO.queryDiyCarBtrPartsName(l).stream())
                .collect(Collectors.toSet());
    }
}